#pragma once

#include <cftf/net/msghdr.h>

namespace cftc::reflect
{
    class Class;
    class Type;
    class Object;
    class Field;
}

namespace cs
{

    class MsgDescriptor
    {
    public:

        const static unsigned short MAX_MSG_ID = 1048;

        template<typename T>
        static unsigned short FindMsgId(T*)
        {
            static unsigned short msg_id = 
                []() -> unsigned short
                {
                    auto meta_class = T::GetStaticClass();
                    return FindMsgIdWithMetaClass(meta_class);
                }();
            return msg_id;
        }
        
        static unsigned short FetchMsgId(const cftf::net::MsgHdr* hdr)
        {
            if (sizeof(ClientPktHeader) <= hdr->GetBodyLen()) {
                ClientPktHeader* header = (ClientPktHeader*)(hdr->GetBodyData());
                return header->cmd;
            }
            return 0;
        }

        static unsigned int CalcSerializeMsgByteSize(cftc::reflect::Object* msg);
        static bool SerializeMsg(cftc::reflect::Object* msg, unsigned char* buf, unsigned int buflen, unsigned int& offset);

        template<typename T>
        static bool DecodeMsg(cftf::net::MsgHdr* hdr)
        {
            if (sizeof(ClientPktHeader) > hdr->GetBodyLen()) {
                return false;
            }
            auto msg = std::make_shared<T>();
            unsigned int offset = 0;
            DecodeMsg(hdr->GetBodyData() + sizeof(ClientPktHeader), hdr->GetBodyLen() - sizeof(ClientPktHeader), offset, msg.get());
            hdr->SetProcessedData(msg);
            return true;
        }

        static bool DecodeMsg(const char* buf, unsigned int buflen, unsigned int& offset, cftc::reflect::Object* obj);

    private:
        static unsigned short FindMsgIdWithMetaClass(const cftc::reflect::Class* meta_class);
        static bool DecodeField(const char* buf, unsigned int buflen, unsigned int& offset,
            void* data, const std::shared_ptr<cftc::reflect::Type>& type, const std::shared_ptr<cftc::reflect::Field>& field);
        static unsigned int CalcSerializeFieldByteSize(const void* data, const std::shared_ptr<cftc::reflect::Type>& type, const std::shared_ptr<cftc::reflect::Field>& field);
        static bool SerializeField(void* data, const std::shared_ptr<cftc::reflect::Type>& type, const std::shared_ptr<cftc::reflect::Field>& field,
            unsigned char* buf, unsigned int buflen, unsigned int& offset);
    };

}